﻿using DA6502.CPU;
using DA6502.Exception;
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;

namespace DA6502.Controls
{
    using addr = System.UInt16;

    public partial class MemoryView : UserControl, IBusDevice
    {
        #region ==============================| 类属性 |==============================
        DataTable memory;

        public bool Dirty = false;

        public addr BaseAddr = 0;

        public ToolStripProgressBar ProgressBar = null;

        public DataTable Memory { get; }

        public bool DataGridViewVisible
        {
            get => dgvMem.Visible;
            set => dgvMem.Visible = value;
        }

        private DataGridViewCellStyle[] CellStyles;
        #endregion

        #region ==============================| 类相关方法 |==============================

        public MemoryView()
        {
            InitializeComponent();

            //  Default Cell Styles
            CellStyles = new DataGridViewCellStyle[7] {
                new DataGridViewCellStyle(),
                new DataGridViewCellStyle(),
                new DataGridViewCellStyle(),
                new DataGridViewCellStyle(),
                new DataGridViewCellStyle(),
                new DataGridViewCellStyle(),
                new DataGridViewCellStyle()
            };
            CellStyles[(int)CELL_TYPE.UNKNOWN] = dgvMem.DefaultCellStyle;
            CellStyles[(int)CELL_TYPE.INSTRUCTION].Font = new Font(dgvMem.DefaultCellStyle.Font, FontStyle.Bold);
            CellStyles[(int)CELL_TYPE.OPERATOR].Font = new Font(dgvMem.DefaultCellStyle.Font, FontStyle.Italic);
            CellStyles[(int)CELL_TYPE.OPERATOR].ForeColor = Color.LightGray;
            CellStyles[(int)CELL_TYPE.VARIABLE].ForeColor = Color.Blue;
            CellStyles[(int)CELL_TYPE.VARIABLE].BackColor = Color.LightGray;
            CellStyles[(int)CELL_TYPE.ARRAY].ForeColor = Color.DarkBlue;
            CellStyles[(int)CELL_TYPE.ARRAY].BackColor = Color.LightGray;
            CellStyles[(int)CELL_TYPE.STACK].Font = new Font(dgvMem.DefaultCellStyle.Font, FontStyle.Bold);
            CellStyles[(int)CELL_TYPE.STACK].ForeColor = Color.Green;
            CellStyles[(int)CELL_TYPE.STACK].BackColor = Color.LightGray;
            CellStyles[(int)CELL_TYPE.PORT].Font = new Font(dgvMem.DefaultCellStyle.Font, FontStyle.Bold);
            CellStyles[(int)CELL_TYPE.PORT].ForeColor = Color.Red;
            CellStyles[(int)CELL_TYPE.PORT].BackColor = Color.Black;
        }

        #endregion

        #region ==============================| 界面相关 |==============================
        public DataGridViewCell CurrentCell
        {
            get => dgvMem.CurrentCell;
        }

        public addr CurrentRow
        {
            get => (ushort)dgvMem.CurrentRow.Index;
        }

        public void JumpTo(addr row)
        {
            if (dgvMem.Rows.Count > row)
                dgvMem.CurrentCell = dgvMem.Rows[row].Cells[0];
        }

        private void SetCellStyle(DataGridViewRow row, CELL_TYPE t)
        {
            row.DefaultCellStyle = CellStyles[(int)t];
        }

        public void SetCellStyle(addr ad)
        {
            CELL_TYPE t = (CELL_TYPE)memory.Rows[ad]["CellType"];
            SetCellStyle(ad, t);
        }

        public void SetCellStyle(addr ad, CELL_TYPE t)
        {
            SetCellStyle(dgvMem.Rows[ad], t);
        }
        #endregion

        #region ==============================| 用户事件回调 |==============================

        /// <summary>
        /// 右键菜单 - 反汇编单行 事件回调
        /// </summary>
        public void CmiSingle_Click(object sender, System.EventArgs e) => ReverseSingleLine?.Invoke((ushort)dgvMem.CurrentRow.Index);

        /// <summary>
        /// 右键菜单 - 反汇编请多行 事件回调
        /// </summary>
        public void CmiBatch_Click(object sender, System.EventArgs e) => ReverseMultiLine?.Invoke((ushort)dgvMem.CurrentRow.Index);

        /// <summary>
        /// 右键菜单弹出事件回调<br></br>
        /// 若DataGridView行数为0则取消菜单弹出
        /// </summary>
        public void Ctm_Opening(object sender, CancelEventArgs e) => e.Cancel = dgvMem.RowCount == 0;

        /// <summary>
        /// 右键菜单 - 标注地址为未知 事件回调
        /// </summary>
        public void CmiMarkAsU_Click(object sender, System.EventArgs e) => MarkCellAs(CELL_TYPE.UNKNOWN);

        /// <summary>
        /// 右键菜单 - 标注地址为指令 事件回调
        /// </summary>
        public void CmiMarkAsI_Click(object sender, System.EventArgs e) => MarkCellAs(CELL_TYPE.INSTRUCTION);

        /// <summary>
        /// 右键菜单 - 标注地址为操作数 事件回调
        /// </summary>
        public void CmiMarkAsO_Click(object sender, System.EventArgs e) => MarkCellAs(CELL_TYPE.OPERATOR);

        /// <summary>
        /// 右键菜单 - 标注地址为变量 事件回调
        /// </summary>
        public void CmiMarkAsV_Click(object sender, System.EventArgs e) => MarkCellAs(CELL_TYPE.VARIABLE);

        /// <summary>
        /// 右键菜单 - 标注地址为数组 事件回调
        /// </summary>
        public void CmiMarkAsA_Click(object sender, System.EventArgs e) => MarkCellAs(CELL_TYPE.ARRAY);

        /// <summary>
        /// 右键菜单 - 标注地址为栈 事件回调
        /// </summary>
        public void CmiMarkAsS_Click(object sender, System.EventArgs e) => MarkCellAs(CELL_TYPE.STACK);

        /// <summary>
        /// 右键菜单 - 标注地址为端口 事件回调
        /// </summary>
        public void CmiMarkAsP_Click(object sender, System.EventArgs e) => MarkCellAs(CELL_TYPE.PORT);

        /// <summary>
        /// 将选中的地址标注为指定的类型
        /// </summary>
        /// <param name="t">要标注的类型</param>
        private void MarkCellAs(CELL_TYPE t)
        {
            SelectRowsByCells();
            foreach (DataGridViewRow row in dgvMem.SelectedRows)
            {
                row.Cells["CellType"].Value = t;
                SetCellStyle(row, t);
            }

        }

        /// <summary>
        /// 鼠标按下事件<br></br>
        /// 此处只实现鼠标右键选中单元功能
        /// </summary>
        private void OnCellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.RowIndex >= 0 && e.Button == System.Windows.Forms.MouseButtons.Right)
                dgvMem.CurrentCell = dgvMem.Rows[e.RowIndex].Cells[e.ColumnIndex];
        }

        /// <summary>
        /// DataGridView的RowPostPaint事件回调，用于刷新单元格的格式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SetCellStyle(object sender, DataGridViewRowPostPaintEventArgs e)
        {
            SetCellStyle((ushort)e.RowIndex);
        }

        /// <summary>
        /// 鼠标悬停时显示ToolTip事件
        /// </summary>
        private void OnCellToolTipTextNeeded(object sender, DataGridViewCellToolTipTextNeededEventArgs e)
        {
            if ((e.ColumnIndex == 2 || e.ColumnIndex == 3) && (CELL_TYPE)memory.Rows[e.RowIndex]["CellType"] == CELL_TYPE.INSTRUCTION)
            {
                byte cmd = (byte)memory.Rows[e.RowIndex]["Value"];
                e.ToolTipText = CommandDictionary.CommandCodes[cmd].ToolTip;
            }
        }

        #endregion

        #region ==============================| 数据相关方法 |==============================

        /// <summary>
        /// 设置一个内存单元
        /// </summary>
        /// <param name="address">地址</param>
        /// <param name="value">值</param>
        /// <param name="instruction">反汇编后的CPU指令</param>
        /// <param name="comment">注释</param>
        /// <param name="cellType">内存内容类型</param>
        public void SetRow(addr address, byte value, string instruction, string comment, CELL_TYPE cellType)
        {
            memory.Rows[address]["Address"] = address;
            memory.Rows[address]["Value"] = value;
            memory.Rows[address]["Instruction"] = instruction;
            memory.Rows[address]["Comment"] = comment;
            memory.Rows[address]["CellType"] = cellType;
            Dirty = true;
        }

        /// <summary>
        /// 设置一个内存单元的指令字段
        /// </summary>
        /// <param name="ad">地址</param>
        /// <param name="ins">指令</param>
        public void SetInstruction(addr ad, string ins)
        {
            memory.Rows[ad]["Instruction"] = ins;
            Dirty = true;
        }

        /// <summary>
        /// 取得一个内存单元的指令字段
        /// </summary>
        /// <param name="ad">地址</param>
        /// <returns>指令</returns>
        public string GetInstruction(addr ad) => memory.Rows[ad]["Instruction"] == DBNull.Value ? string.Empty : (string)memory.Rows[ad]["Instruction"];

        /// <summary>
        /// 设置一个内存单元的注释字段
        /// </summary>
        /// <param name="ad">地址</param>
        /// <param name="com">注释</param>
        /// <exception cref="OutOfRangeException">若注释长于255字节则抛出此异常</exception>
        public void SetComment(addr ad, string com)
        {
            if (com.Length > byte.MaxValue) throw new OutOfRangeException(@"注释每行最多只支持255字节");
            memory.Rows[ad]["Comment"] = com;
            Dirty = true;
        }

        /// <summary>
        /// 取得一个内存单元的注释字段
        /// </summary>
        /// <param name="ad">地址</param>
        /// <returns>注释</returns>
        public string GetComment(addr ad) => memory.Rows[ad]["Comment"] == DBNull.Value ? string.Empty : (string)memory.Rows[ad]["Comment"];

        /// <summary>
        /// 设置一个内存单元的类型字段
        /// </summary>
        /// <param name="ad">地址</param>
        /// <param name="t">类型</param>
        public void SetCellType(addr ad, CELL_TYPE t)
        {
            memory.Rows[ad]["CellType"] = t;
            if (dgvMem.DataSource != null) SetCellStyle(ad, t);
        }

        /// <summary>
        /// 取得一个内存单元的类型字段
        /// </summary>
        /// <param name="ad">地址</param>
        /// <returns>类型</returns>
        public CELL_TYPE GetCellType(addr ad) => (CELL_TYPE)memory.Rows[ad]["CellType"];

        /// <summary>
        /// 设置一个内存单元的指令字长字段
        /// </summary>
        /// <param name="ad">地址</param>
        /// <param name="length">指令字长</param>
        public void SetLength(addr ad, int length) => memory.Rows[ad]["Length"] = length;

        /// <summary>
        /// 取得一个内存单元的指令字长字段
        /// </summary>
        /// <param name="ad">地址</param>
        /// <returns>指令字长</returns>
        public int GetLength(addr ad) => (int)memory.Rows[ad]["Length"];

        /// <summary>
        /// 清空内存，之后如果需要再次访问，需要重新用InitializeMemory()方法初始化内存
        /// </summary>
        public void Clear()
        {
            if (memory != null)
            {
                memory.Clear();
                memory.Dispose();
                memory = null;
                Dirty = false;
            }
        }

        /// <summary>
        /// 重置内存
        /// </summary>
        public void Reset()
        {
            Clear();
            InitializeMemory();
            InitializeDataGridView();
        }

        /// <summary>
        /// 从内存中读一个字节
        /// </summary>
        /// <param name="address">内存地址</param>
        /// <returns></returns>
        public byte ReadByte(addr address) => (byte)memory.Rows[address]["Value"];

        /// <summary>
        /// 写一个字节到内存
        /// </summary>
        /// <param name="address">内存地址</param>
        /// <param name="value">要写入的值</param>
        public void WriteByte(ushort address, byte value) => memory.Rows[address]["Value"] = value;

        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="index">内存地址</param>
        public byte this[addr ad]
        {
            get => ReadByte(ad);
            set => WriteByte(ad, value);
        }

        /// <summary>
        /// 加载一段映像到内存
        /// </summary>
        /// <param name="baseAddr">内存起始地址</param>
        /// <param name="img">映像</param>
        /// <exception cref="Exception.OutOfRangeException"></exception>
        public void LoadImage(addr baseAddr, byte[] img)
        {
            Clear();
            InitializeMemory();
            InitializeDataGridView();
            BaseAddr = baseAddr;
            addr offset = (ushort)img.Length;
            if ((baseAddr + offset) > 0x10000)
                throw new OutOfRangeException(@"基地址"
                                              + baseAddr.ToString("X4")
                                              + "+映像长度"
                                              + baseAddr.ToString("X4")
                                              + "造成内存溢出");

            if (ProgressBar != null)
            {
                ProgressBar.Maximum = img.Length;
                ProgressBar.Visible = true;
            }
            for (int i = 0; i < img.Length; i++)
            {
                memory.Rows[BaseAddr + i]["Address"] = BaseAddr + i;
                memory.Rows[BaseAddr + i]["Value"] = img[i];
                memory.Rows[BaseAddr + i]["Instruction"] = string.Empty;
                memory.Rows[BaseAddr + i]["Comment"] = string.Empty;
                memory.Rows[BaseAddr + i]["Length"] = 0;
                memory.Rows[BaseAddr + i]["CellType"] = CELL_TYPE.UNKNOWN;
                if (ProgressBar != null) ProgressBar.Value = i;
            }
            if (ProgressBar != null) ProgressBar.Visible = false;
        }
        #endregion

        #region ==============================| 委托与事件 |==============================
        public delegate void ReverseSingleLineHandler(addr ad);
        public delegate void ReverseMultiLineHandler(addr ad);

        public event ReverseMultiLineHandler ReverseMultiLine;
        public event ReverseSingleLineHandler ReverseSingleLine;
        #endregion

        #region ==============================| 内存相关 |==============================
        /// <summary>
        /// 初始化内存<br></br>
        /// 初始化完成后内存将有16K字节的空白存储单元
        /// </summary>
        public void InitializeMemory()
        {
            if (memory != null) Clear();

            memory = new DataTable();
            DataColumn col;

            col = new DataColumn("Address", typeof(ushort));
            col.AllowDBNull = false;
            memory.Columns.Add(col);

            col = new DataColumn("Value", typeof(byte));
            col.AllowDBNull = false;
            memory.Columns.Add(col);

            col = new DataColumn("Instruction", typeof(string));
            col.AllowDBNull = true;
            col.MaxLength = 255;
            memory.Columns.Add(col);

            col = new DataColumn("Comment", typeof(string));
            col.AllowDBNull = true;
            col.MaxLength = 255;
            memory.Columns.Add(col);

            col = new DataColumn("Length", typeof(int));
            col.AllowDBNull = true;
            memory.Columns.Add(col);

            col = new DataColumn("CellType", typeof(CELL_TYPE));
            col.AllowDBNull = false;
            col.DefaultValue = CELL_TYPE.UNKNOWN;
            memory.Columns.Add(col);

            for (int i = 0; i < 0x10000; i++)
            {
                memory.Rows.Add((ushort)i, 0x00, string.Empty, string.Empty);
            }
        }

        #endregion

        #region ==============================| DataGrivView相关 |==============================

        private void SelectRowsByCells()
        {
            ArrayList rows = new ArrayList();
            foreach (DataGridViewCell c in dgvMem.SelectedCells)
            {
                if (!rows.Contains(c.RowIndex)) rows.Add(c.RowIndex);
            }

            foreach (int r in rows)
            {
                dgvMem.Rows[r].Selected = true;
            }
        }

        /// <summary>
        /// 初始化DataGridView<br></br>
        /// 初始化完成后DataGridView将有5个列，并将数据源绑定至Memory
        /// </summary>
        public void InitializeDataGridView()
        {
            DataGridViewTextBoxColumn col;

            dgvMem.Columns.Clear();
            dgvMem.AutoGenerateColumns = false;

            col = new DataGridViewTextBoxColumn();
            col.Name = "CellType";
            col.DataPropertyName = "CellType";
            col.HeaderText = "类型";
            dgvMem.Columns.Add(col);

            col = new DataGridViewTextBoxColumn();
            col.Name = "Address";
            col.DataPropertyName = "Address";
            col.HeaderText = "地址";
            col.DefaultCellStyle.Format = "X4";
            dgvMem.Columns.Add(col);

            col = new DataGridViewTextBoxColumn();
            col.Name = "Value";
            col.DataPropertyName = "Value";
            col.HeaderText = "值";
            col.DefaultCellStyle.Format = "X2";
            dgvMem.Columns.Add(col);

            col = new DataGridViewTextBoxColumn();
            col.Name = "Instruction";
            col.DataPropertyName = "Instruction";
            col.HeaderText = "指令";
            dgvMem.Columns.Add(col);

            col = new DataGridViewTextBoxColumn();
            col.Name = "Comment";
            col.DataPropertyName = "Comment";
            col.HeaderText = "注释";
            col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dgvMem.Columns.Add(col);

            dgvMem.RowTemplate.Height = 20;
            dgvMem.Enabled = true;
            dgvMem.AutoGenerateColumns = false;

            BindDataTable();

        }

        /// <summary>
        /// 绑定数据源
        /// </summary>
        public void BindDataTable()
        {
            dgvMem.DataSource = memory;
            dgvMem.AutoGenerateColumns = false;
        }

        public void UnbindDataTable() => dgvMem.DataSource = null;

        #endregion

    }


    /// <summary>
    /// 当前内存单元内容类别枚举
    /// </summary>
    public enum CELL_TYPE : byte
    {
        /// <summary>
        /// 未知类别
        /// </summary>
        UNKNOWN = 0,

        /// <summary>
        /// 指令
        /// </summary>
        INSTRUCTION = 1,

        /// <summary>
        /// 操作数
        /// </summary>
        OPERATOR = 2,

        /// <summary>
        /// 变量
        /// </summary>
        VARIABLE = 3,

        /// <summary>
        /// 查表数组
        /// </summary>
        ARRAY = 4,

        /// <summary>
        /// 栈
        /// </summary>
        STACK = 5,

        /// <summary>
        /// IO端口
        /// </summary>
        PORT = 6
    }
}